package com.tetris;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.net.URL;
import java.util.Random;

import javax.swing.Timer;

// 游戏
@SuppressWarnings({ "deprecation", "removal" })
class Game implements ActionListener {
    // 回调
    public interface Callback {
        void onDifficultyUp(int difficulty);    // 难度增加事件
        void onGameOver(boolean isWon);         // 游戏结束事件
        void onScore(int score);                // 得分事件
    }

    // 状态
    public enum Status {
        END,    // 已结束
        PAUSED, // 暂停中
        PLAYING // 游戏中
    }

    private final Callback mCallback;               // 回调
    private Block mCurBlock;                        // 当前方块
    private int mDifficulty;                        // 难度
    private boolean mEnableMusic;                   // 音乐开关
    private final Component mGameBoard;             // 游戏区域
    private final Graphics mGameBoardDrawer;        // 游戏区域绘图者
    private final AudioClip mMusicPlayer;           // 音乐播放者
    private Block mNextBlock;                       // 下一个方块
    private final Component mPreview;               // 预览区域
    private final Graphics mPreviewDrawer;          // 预览区域绘图者
    private final Random mRandom;                   // 随机数生成器
    private int mScore;                             // 得分
    private Status mStatus;                         // 状态
    private final Timer mTimer;                     // 定时器
    private int mTop;                               // 有方块的最高行
    public static ColorGrid[][] sExistentBlocks;    // 累积的方块

    /**
     * 构造方法。
     * @param callback          回调
     * @param gameBoard         游戏区域
     * @param gameBoardDrawer   游戏区域绘图者
     * @param preview           预览区域
     * @param previewDrawer     预览区域绘图者
     */
    public Game(Callback callback, Component gameBoard, Graphics gameBoardDrawer, Component preview, Graphics previewDrawer) {
        // 初始化成员
        mCallback = callback;
        mDifficulty = 1;
        mEnableMusic = false;
        mGameBoard = gameBoard;
        mGameBoardDrawer = gameBoardDrawer;
        URL url = null;
        try {
            url = new File(Const.MUSIC_FILE_NAME).toURL();
        } catch (Exception e) {
            e.printStackTrace();
        }
        mMusicPlayer = Applet.newAudioClip(url);
        mPreview = preview;
        mPreviewDrawer = previewDrawer;
        mRandom = new Random();
        mScore = 0;
        mStatus = Status.END;
        mTimer = new Timer(Const.TIMER_INIT, this);
        mTop = Const.GAME_BOARD_GRID_COUNT_V + 1;

        // 画游戏区域
        this.drawGameBoard();

        // 画预览区域
        this.drawPreview();
    }

    /**
     * 定时器事件的响应方法。
     * @param e 事件相关信息
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        this.moveBlockDown();
    }

    /**
     * 检查是否存在满行。
     */
    private void checkRow() {
        // 更新"有方块的最高行"
        final int top = mCurBlock.getTop();
        if (top < mTop) {
            mTop = top;
        }

        int released = 0;    // 消除的行数
        final int bottom = mCurBlock.getBottom();

        // 从有方块的最上面一行，到当前方块所占的最下一行，逐行检查
        for (int i = mTop; i <= bottom; i++) {
            for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                // 如果该行有空的，就跳过该行
                if (!sExistentBlocks[i][j].mUsed) {
                    break;
                }
                // 如果该行满了，消除该行
                if (j == Const.GAME_BOARD_GRID_COUNT_H - 1) {
                    this.releaseRow(i);
                    released++;
                }
            }
        }

        if (released > 0) {
            // 因为消行了，所以需要重绘
            this.drawGameBoard();
            this.drawExistentBlock();

            // 计算得分
            mScore += released * released * 100;

            // 发送通知
            mCallback.onScore(mScore);

            // 计算难度
            if (mScore / Const.DIFF_BASE + 1 > mDifficulty && mDifficulty < Const.DIFF_MAX) {
                // 增加难度
                mDifficulty++;
                mTimer.stop();
                mTimer.setDelay(Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1));
                mTimer.start();

                // 发送通知
                mCallback.onDifficultyUp(mDifficulty);
            }
        }
    }
    
    /**
     * 随机创建一种方块。
     * @return 方块
     */
    private Block createRandBlock() {
        final int random = Math.abs(mRandom.nextInt() % 7) + 1;

        switch (random) {
            case 1:
                return new Block1();
            case 2:
                return new Block2();
            case 3:
                return new Block3();
            case 4:
                return new Block4();
            case 5:
                return new Block5();
            case 6:
                return new Block6();
            case 7:
                return new Block7();
        }

        return new Block1();
    }

    /**
     * 画累积的方块。
     */
    private void drawExistentBlock() {
        for (int i = 0; i < Const.GAME_BOARD_GRID_COUNT_V; i++) {
            for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                if (sExistentBlocks[i][j].mUsed) {
                    mGameBoardDrawer.setColor(sExistentBlocks[i][j].mColor);
                    mGameBoardDrawer.fillRect(Const.GRID_SIZE * j, Const.GRID_SIZE * i, Const.GRID_SIZE, Const.GRID_SIZE);
                    Block.drawEdge(mGameBoardDrawer, Const.GRID_SIZE * j, Const.GRID_SIZE * i);
                }
            }
        }
    }

    /**
     * 画游戏区域。
     */
    private void drawGameBoard() {
        mGameBoardDrawer.setColor(Const.GAME_BOARD_COLOR);
        mGameBoardDrawer.fillRect(0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT);
        mGameBoard.repaint();
    }

    /**
     * 画预览区域。
     */
    private void drawPreview() {
        mPreviewDrawer.setColor(Const.PREVIEW_COLOR);
        mPreviewDrawer.fillRect(0, 0, Const.PREVIEW_WIDTH, Const.PREVIEW_HEIGHT);
        if (mNextBlock != null) {
            mNextBlock.enterPreview(mPreviewDrawer);
        }
        mPreview.repaint();
    }

    /**
     * 游戏结束。
     * @param isWon  胜利则为 true，否则为 false
     */
    private void gameOver(boolean isWon) {
        // 将状态设置为已结束
        mStatus = Status.END;

        // 停止定时器
        mTimer.stop();

        // 停止播放音乐
        if (mEnableMusic) {
            mMusicPlayer.stop();
        }

        // 发送通知
        mCallback.onGameOver(isWon);
    }    

    /**
     * 获取游戏状态。
     * @return 游戏状态
     */
    public Status getStatus() {
        return mStatus;
    }

    /**
     * 判断是否失败。
     * @return 失败则返回 true，否则返回 false
     */
    private boolean isLost() {
        return (mTop == 0 && sExistentBlocks[0][4].mUsed);
    }
    
    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private boolean isWon() {
        return mScore >= Const.WIN_NUMBER;
    }

    /**
     * 方块下落。
     */
    private void moveBlockDown() {
        if (mStatus != Status.PLAYING) {
            return;
        }

        final boolean canMoveDown = mCurBlock.moveDown();
        if (!canMoveDown) {
            // 检查是否存在满行
            this.checkRow();
            
            // 判断是否胜利
            if (this.isWon()) {
                this.gameOver(true);
                return;
            }

            // 判断是否失败
            if (this.isLost()) {
                this.gameOver(false);
                return;
            }

            // 产生新的方块
            this.nextBlock();
        }
    }

    /**
     * 产生新的方块。
     */
    private void nextBlock() {
        // 创建当前方块
        if (mCurBlock != null) {
            mCurBlock = mNextBlock;
        } else {
            mCurBlock = this.createRandBlock();
        }

        // 创建下一个方块
        mNextBlock = this.createRandBlock();

        // 当前方块进入游戏区域
        mCurBlock.enterGameBoard(mGameBoard, mGameBoardDrawer);

        // 下一个方块进入预览区域
        this.drawPreview();
    }

    /**
     * 按键事件的响应方法。
     * @param key   按键
     */
    public void onKeyPressed(int key) {
        if (mStatus != Status.PLAYING) {
            return;
        }

        switch (key) {
            case KeyEvent.VK_DOWN:
                this.moveBlockDown();
                break;
            case KeyEvent.VK_LEFT:
                mCurBlock.moveLeft();
                break;
            case KeyEvent.VK_RIGHT:
                mCurBlock.moveRight();
                break;            
            case KeyEvent.VK_UP:
                mCurBlock.transform();
                break;
        }
    }

    /**
     * 游戏暂停。
     */
    public void pause() {
        if (mStatus == Status.PLAYING) {
            // 停止定时器
            mTimer.stop();
            
            // 停止播放音乐
            if (mEnableMusic) {
                mMusicPlayer.stop();
            }

            // 将状态设置为已暂停
            mStatus = Status.PAUSED;
        } else if (mStatus == Status.PAUSED) {
            // 播放音乐
            if (mEnableMusic) {
                mMusicPlayer.loop();
            }            

            // 开启定时器
            mTimer.setDelay(Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1));
            mTimer.start();

            // 将状态设置为游戏中
            mStatus = Status.PLAYING;
        }
    }
    
    /**
     * 播放音乐。
     * @return 正在播放音乐则为 true，否则为 false
     */
    public boolean playMusic() {
        mEnableMusic = !mEnableMusic;

        if (mEnableMusic) {
            if (mStatus == Status.PLAYING) {
                mMusicPlayer.loop();
            }
        } else {
            mMusicPlayer.stop();
        }

        return mEnableMusic;
    }

    /**
     * 消行。
     * @param row   行号
     */
    private void releaseRow(int row) {
        // 最上行
        if (row == 0) {
            for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                sExistentBlocks[row][j].mUsed = false;
            }
            mTop++;
            return;
        }

        // 非最上行
        for (int i = row; i > mTop; i--) {
            for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                sExistentBlocks[i][j].mUsed = sExistentBlocks[i - 1][j].mUsed;
                sExistentBlocks[i][j].mColor = sExistentBlocks[i - 1][j].mColor;
            }
        }

        for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
            sExistentBlocks[mTop][j].mUsed = false;
        }

        mTop++;
    }

    /**
     * 游戏开始。
     */
    public void start() {
        // 画游戏区域
        this.drawGameBoard();

        // 画预览区域
        this.drawPreview();

        // 创建方块
        sExistentBlocks = new ColorGrid[Const.GAME_BOARD_GRID_COUNT_V][Const.GAME_BOARD_GRID_COUNT_H];
        for (int i = 0; i < Const.GAME_BOARD_GRID_COUNT_V; i++) {
            for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                sExistentBlocks[i][j] = new ColorGrid();
            }
        }
        mCurBlock = null;
        mNextBlock = null;
        this.nextBlock();
        
        // 重置难度
        mDifficulty = 1;

        // 重置分数
        mScore = 0;

        // 开启定时器
        mTimer.stop();
        mTimer.setDelay(Const.TIMER_INIT);
        mTimer.start();
        
        // 播放音乐
        if (mEnableMusic) {
            mMusicPlayer.loop();
        }        

        // 将状态设置为游戏中
        mStatus = Status.PLAYING;
    }
}
